package org.apache.click.doubleclick.injectspring;

import org.apache.click.doubleclick.inject.Cache;
import org.apache.click.doubleclick.inject.ContextResolver;
import org.apache.click.doubleclick.inject.DependencyInjector;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Field;
import java.util.List;

/**
 * Responsible for spring dependency injection.
 *
 * @author Jahid Shohel(development.jsh@gmail.com)
 */

public class SpringDependencyInjector implements DependencyInjector {
    private ContextResolver<ApplicationContext> contextResolver;
    private Cache cache;

    public SpringDependencyInjector() {
        this.contextResolver = new SpringContextResolverImpl<ApplicationContext>();
        this.cache = new Cache();
    }

    /**
     * {@inheritDoc}
     */
    public <T> T injectDependencies(T target) {
        //TODO: instead of passing false, check in the annotation. now its hard coded
        return injectDependencies(target, false);
    }

    /**
     * {@inheritDoc}
     */
    public <T> T injectDependencies(T target, boolean injectProxy) {
        if (target == null) {
            throw new RuntimeException("Target can not be null when injecting dependencies");
        }

        List<Field> fields = cache.getAnnotatedFields(target.getClass());

        for (Field field : fields) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }

            try {
                if (field.get(target) == null) {
                    Class<?> fieldType = field.getType();
                    if (injectProxy) {
                        //TODO: generate and inject proxy
                    }
                    Object value = getBean(fieldType);
                    field.set(target, value);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Error happened when trying to set bean for type: " + field.getType(), e);
            }
        }
        return target;
    }

    /**
     * {@inheritDoc}
     */
    public <T> T getBean(Class<T> beanClass) {
        ApplicationContext applicationContext = contextResolver.resolveDependencyInjectorContext();
        String[] beanNames = applicationContext.getBeanNamesForType(beanClass);

        if (beanNames.length == 0) {
            throw new RuntimeException("No bean found for type: " + beanClass);
        } else if (beanNames.length > 1) {
            //TODO: if more then one type found, then we look for name(name declared on annotation)
            throw new RuntimeException("More then one bean found for type: " + beanClass.getSimpleName());
        }
        return (T) applicationContext.getBean(beanNames[0], beanClass);
    }

    /**
     * {@inheritDoc}
     */
    public <T> T getBeanAsProxy(Class<T> beanClass) {
        // TODO: for lazy init with proxy
        return null;
    }

//    private String getBeanName(Field field) {
//		InjectBean annotation = field.getAnnotation(InjectBean.class);
//
//		String name = annotation.name();
//		if (name==null || name.length()==0)	{
//			name = beanNameCache.get(field.getType());
//			if (name == null) {
//				name = getBeanNameOfClass(contextLocator.getSpringContext(), field.getType());
//				beanNameCache.put(field.getType(), name);
//			}
//		}
//		return name;
//	}
}